using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.GamePlay;
using NetProtocol.Enum;
using NetProtocol.POD;
using System;
using System.Collections.Generic;
using LFloatMath.Math;
using Random = LFloatMath.Math.Random;

namespace IQIGame.Onigao.Logic
{
    public class Battle : IPODFactory, IReusableClass
    {
        private int idCreate = 1000;
        public int CreateID => idCreate++;

        public CreateBattlePOD enterPOD { get; private set; }
        /// <summary>
        /// 玩家集合
        /// </summary>
        private List<BattlePlayer> m_Players;
        public List<ISession> AllSessions { get; private set; }
        private long _hostPlayerId;
        private bool _allPlayerEntered;

        /// <summary>
        /// 单机战斗唯一的玩家或多人战斗房主。
        /// </summary>
        public BattlePlayer leaderPlayer => m_Players != null && m_Players.Count > 0 ? m_Players[0] : null;

        public List<BattlePlayer> players => m_Players;

        private Action<BattleEndData> onBattleEnd;
        public long id { get; private set; }
        public int frame { get; private set; }

        public bool isEnd { get; private set; }
        public bool isLocal => enterPOD.isLocal;

        private BattlePlayController _mPlayCtrl;
        public BattlePlayController playCtrl => this._mPlayCtrl;

        /// <summary>
        /// 随机数生成器
        /// </summary>
        public Random random { get; private set; }

        public BattleType battleType { get; private set; }

        public uint MaxStore => 100;

        private bool _isActive;

        public void Active()
        {
            _isActive = true;
            this.playCtrl.StartBattle();
        }

        public void LogicOnUpdate()
        {
            if (!_isActive)
            {
                return;
            }

            try
            {
                frame++;
            }
            catch (Exception e)
            {
                LogicLog.LogError(e);
            }
        }

        public void Init(CreateBattlePOD createPOD, Action<BattleEndData> onBattleEnd)
        {
            this.enterPOD = createPOD;
            this.random = new Random((uint)createPOD.randomSeed);
            this.idCreate = this.random.Range(1000, 10000);
            this.id = createPOD.id;
            this.battleType = createPOD.battleType;

            //先创建所有的玩家，但这时还没有完整数据
            //但后续需要再AddPlayer的地方判断玩家是否到齐，这里必须拥有完整的玩家列表
            this.m_Players = new List<BattlePlayer>();
            this.AllSessions = new List<ISession>();
            //先创建房主并放到第一个
            BattlePlayer player = ClassPool.Get<BattlePlayer>();
            player.InitId(createPOD.hostPlayerId, this);
            this.m_Players.Add(player);
            this._hostPlayerId = createPOD.hostPlayerId;
            foreach (long pid in createPOD.allPlayers)
            {
                if (pid != _hostPlayerId && pid != 0)
                {
                    player = ClassPool.Get<BattlePlayer>();
                    player.InitId(pid, this);
                    this.m_Players.Add(player);
                }
            }

            this._mPlayCtrl = new BattlePlayController();
            this._mPlayCtrl.Initialize(this, createPOD);
            this.onBattleEnd = onBattleEnd;
        }

        public void Dispose()
        {
            this._mPlayCtrl?.Dispose();
            this._mPlayCtrl = null;
            this.id = 0;
            this.onBattleEnd = null;
            this.frame = 0;
            this.random = null;
        }

        public BattlePlayer GetPlayer(long id)
        {
            for (int i = 0; i < m_Players.Count; i++)
            {
                BattlePlayer player = m_Players[i];
                if (player.id == id)
                {
                    return player;
                }
            }
            return null;
        }


        public bool IsAllPlayersReady()
        {
            bool isAllReady = true;
            for (int i = 0; i < m_Players.Count; i++)
            {
                if (!m_Players[i].isReadyToStart)
                {
                    isAllReady = false;
                    break;
                }
            }
            return isAllReady;
        }

        /// <summary>
        /// 是否有玩家
        /// </summary>
        /// <returns></returns>
        public bool HasPlayers()
        {
            return m_Players?.Count > 0;
        }

        /// <summary>
        /// 初始化玩家
        /// </summary>
        public BattlePlayer InitPlayer(LogicPlayer logicPlayer)
        {
            BattlePlayer player = GetPlayer(logicPlayer.id);
            if (player == null)
            {
                return null;
            }
            player.SetLogicPlayer(logicPlayer);
            return player;
        }

        /// <summary>
        /// 将玩家加入战斗
        /// </summary>
        public void EnterBattle(BattlePlayer player, ISession session)
        {
            player.session = session;
            AllSessions.Add(session);

            //如果所有玩家都已就位，则广播进入关卡消息
            //否则等待最后一个玩家就位后广播
            TryBroadcastEnterBattle();
        }

        public void TryBroadcastEnterBattle()
        {
            if (_allPlayerEntered)
            {
                return;
            }
            //是否所有玩家都已就位
            bool isAllInited = true;
            foreach (BattlePlayer p in m_Players)
            {
                if (p.session == null)
                {
                    isAllInited = false;
                    break;
                }
            }

            //如果所有玩家都已就位，向所有玩家发送Enter的消息，否则忽略本次消息
            if (!isAllInited)
            {
                return;
            }
            //发送通知
            var rResultPOD = GeneratePOD() as BattleEnterPOD;
            SBattleLogicModule.NetCore.SC_NotifyEnter_Broadcast(AllSessions, rResultPOD);

            _allPlayerEntered = true;
        }


        /// <summary>
        /// 将玩家从战斗中踢掉
        /// </summary>
        public void LeaveBattle(BattlePlayer player)
        {
            m_Players.Remove(player);
            AllSessions.Remove(player.session);
            SBattleLogicModule.NetCore.SC_NotifyLeave(player.session);
        }

#region 客户端发起

        public void CastSkill(BattleSkillPOD battleSkillPod)
        {
            if (this.isEnd)
            {
                return;
            }
            this.playCtrl.CastSkill(battleSkillPod, null);
        }

        public void SkillComplete()
        {
            if (this.isEnd)
            {
                return;
            }
            this.playCtrl?.SkillComplete();
        }

        public bool AutoBattle(BattlePlayer player, BattleAutoPOD openPOD)
        {
            if (this.isEnd)
            {
                return false;
            }
            return this.playCtrl?.AutoBattle(openPOD) ?? false;
        }

        public void Substitute(int oldGuid)
        {
            if (this.isEnd)
            {
                return;
            }
            this.playCtrl?.Substitute(oldGuid);
        }

        public void SkipBattle()
        {
            this._mPlayCtrl.SkipBattle();
        }

#endregion

#region 服务器下发

        public void NotifyRound(bool isRoundEnd)
        {
            // var rQueue = new List<int>();
            // this.playCtrl.queueCtrl.GetTurnQueue(ref rQueue);
            // var rRoundUpdate = BattleUpdateFactory.CreateRound(this.playCtrl.RoundNum, isRoundEnd, rQueue);
            var rUpdate = new List<BattleUpdatePOD>();
            // rUpdate.Add(rRoundUpdate);
            rUpdate.AddRange(this.GetAndClearResult());
            this.NotifyUpdates(rUpdate);
        }

        public void NotifyTurnStart()
        {
            var rUpdate = new List<BattleUpdatePOD>();
            var rQueue = new List<int>();
            this.playCtrl.queueCtrl.GetTurnQueue(ref rQueue);
            rUpdate.Add(BattleUpdateFactory.CreateTurn(false, this.playCtrl.queueCtrl.current.unit.GUID, rQueue));
            rUpdate.AddRange(this.GetAndClearResult());
            this.NotifyUpdates(rUpdate);
        }

        public void NotifyPermitCast(int executer, int skillID)
        {
            var rUpdate = this.GetAndClearResult();
            rUpdate.Add(BattleUpdateFactory.CreatePermitCast(executer, skillID));
            this.NotifyUpdates(rUpdate);
        }

        /// <summary>
        /// 通知使用技能，在Turn中，执行技能才会使用此协议进行通知，触发出的技能仅进入队列不单独通知
        /// </summary>
        /// <param name="executer"></param>
        /// <param name="skill"></param>
        public void NotifySkillCast(Unit executer, Skill skill, LVector2 targetPos)
        {
            var rUpdate = new List<BattleUpdatePOD>();
            rUpdate.Add(BattleUpdateFactory.CreateCastSkill(executer.GUID, skill?.skillID ?? 0, executer.pos, targetPos));
            rUpdate.AddRange(this.GetAndClearResult());
            rUpdate.Add(BattleUpdateFactory.CreateCastSkillComplete(executer.GUID));
            this.NotifyUpdates(rUpdate);
            if (this.playCtrl.skiping)
            {
                this.SkillComplete();
            }
        }

        public void BattleEnd()
        {
            this.isEnd = true;
            // 单独同步updates -- 为了单独同步battleEnd
            List<BattleUpdatePOD> rUpdate = this.GetAndClearResult();
            this.NotifyUpdates(rUpdate);
            var bEnemyAlive = this.playCtrl.unitCtrl.units.Find(
                (x) => x.teamIndex == BattleConst.TeamBlueIndex && !x.IsDead(true, true)) != null;
            var rData = new BattleEndData() // TODO 未处理平局
            {
                successTeam = bEnemyAlive ? BattleConst.TeamBlueIndex : BattleConst.TeamRedIndex,
                battleID = this.id,
                hostPlayerId = this.enterPOD.hostPlayerId,
                battleType = this.battleType,
                players = new List<BattlePlayer>(this.players),
            };
            if (this.playCtrl.teamCtrl.teams.TryGetValue(BattleConst.TeamRedIndex, out var rSelfTeam))
            {
                rData.allMonsterTeam.AddRange(this.playCtrl.teamCtrl.allMonsterTeam);
                for (int i = 0; i < rSelfTeam.units.Count; i++)
                {
                    var rUnit = rSelfTeam.units[i];
                    if (!rUnit.IsTotem())
                    {
                        rUnit.statistics.unit = rUnit.ToUnitPOD();
                        rData.statistics.Add(rUnit.statistics);
                    }
                }
            }
            SBattleLogicModule.NetCore.SC_NotifyEnd_Broadcast(this.AllSessions, rData.GeneratePOD() as BattleEndPOD);

            LogicHelper.OnBattleEnd?.Invoke(rData);
            this.onBattleEnd?.Invoke(rData);
        }

        public void NextWave(BattleTeam rMonsterTeam)
        {
            var rUpdate = this.GetAndClearResult();
            rUpdate.Add(BattleUpdateFactory.CreateNextWave(rMonsterTeam.GeneratePOD() as BattleTeamViewPOD));
            this.NotifyUpdates(rUpdate);
        }

        public void NotifySubstitute(Unit rDeadUnit, Unit rSubUnit)
        {
            var rUpdate = new List<BattleUpdatePOD>();
            rUpdate.Add(BattleUpdateFactory.CreateSubstitute(rDeadUnit.GUID, rSubUnit.GeneratePOD() as BattleUnitViewPOD, rSubUnit.teamIndex));
            rUpdate.AddRange(this.GetAndClearResult());
            this.NotifyUpdates(rUpdate);
        }

        private void NotifyUpdates(List<BattleUpdatePOD> updates)
        {
            if (!this.playCtrl.skiping)
            {
                SBattleLogicModule.NetCore.SC_NotifyUpdates_Broadcast(this.AllSessions, updates);
            }
        }

#endregion

        private List<BattleUpdatePOD> GetAndClearResult()
        {
            return this.playCtrl.resultCtrl.GetResult();
        }

        public IPOD GeneratePOD()
        {
            var rPOD = new BattleEnterPOD();
            rPOD.isLocal = enterPOD.isLocal;
            rPOD.battleType = battleType;
            rPOD.battleId = id;
            for (int i = 0; i < this.playCtrl.teamCtrl.teams.Count; i++)
            {
                var rTeam = this.playCtrl.teamCtrl.teams[i];
                rPOD.teams.Add(rTeam.GeneratePOD() as BattleTeamViewPOD);
            }
            rPOD.battleScene = this.enterPOD.battleScene;
            rPOD.randomSeed = (int)this.random.randSeed;
            return rPOD;
        }

        public void OnReset()
        {
            this.id = 0;
            this._mPlayCtrl = null;
            this.frame = 0;
            this._hostPlayerId = 0;
            if (this.m_Players != null)
            {
                foreach (var player in this.m_Players)
                {
                    ClassPool.Put(player);
                }
                this.m_Players.Clear();
            }
            this.m_Players = null;
            this.AllSessions.Clear();
            this.AllSessions = null;
            this._allPlayerEntered = false;

            this.idCreate = 1000;
            this.enterPOD = default;
            this.isEnd = false;
            this.random = default;
            this._isActive = default;
            this.battleType = default;
        }
    }

    public class BattleEndData : IPODFactory
    {
        public long battleID;
        public int successTeam;
        public long hostPlayerId;
        public BattleType battleType;

        public List<BattlePlayer> players;
        public List<BattleUnitStatistics> statistics = new List<BattleUnitStatistics>();
        public List<int> allMonsterTeam = new List<int>();

        public IPOD GeneratePOD()
        {
            var rPOD = new BattleEndPOD()
            {
                battleId = this.battleID,
                battleType = this.battleType,
                hostPlayerId = this.hostPlayerId,
                successTeam = (BattleTeamId)this.successTeam,
            };
            return rPOD;
        }
        
        /// <summary>
        /// 生成玩家的统计数据
        /// </summary>
        /// <param name="pid">为0表示不筛选</param>
        /// <returns></returns>
        public BattleStatisticsPOD GenStatisticPOD(long pid = 0)
        {
            BattleStatisticsPOD statisticsPod = new BattleStatisticsPOD();
            foreach (var statisticsUnitPOD in this.statistics)
            {
                var unitPOD = statisticsUnitPOD.unit;
                if (unitPOD.pid == 0) continue; // 只有玩家单位生成数据
                if (pid > 0 &&  unitPOD.pid != pid) continue; // 只生成对应玩家的数据
                int hp = unitPOD.attrs[(int)EAttributeType.Hp];
                int sp = unitPOD.attrs[(int)EAttributeType.SP];
                statisticsPod.units.Add(new BattleStatisticsUnitPOD()
                {
                    cid = unitPOD.configID,
                    skinCid = unitPOD.skinID,
                    hp = hp,
                    sp = sp,
                    maxHp = unitPOD.attrs[(int)EAttributeType.MaxHp],
                    maxSp = unitPOD.attrs[(int)EAttributeType.MaxSP],
                    damage = statisticsUnitPOD.damage,
                    cure = statisticsUnitPOD.cure,
                    hurt = statisticsUnitPOD.hurt,
                    isMvp = statisticsUnitPOD.isMvp
                });
            }
            return statisticsPod;
        }
        
    }

    public class BattleUnitStatistics
    {
        public HeroPOD hero;
        public BattleUnitPOD unit;
        public int cid;
        public int hp;
        public int maxHp;
        public int sp;
        public int maxSp;
        public int damage; // 伤害
        public int cure; // 治疗
        public int hurt; // 受击
        public bool isMvp;
    }
}